package org.anyin.gitee.cloud.center.gateway.filter;


import cn.hutool.json.JSONUtil;
import org.anyin.gitee.cloud.center.common.constants.AuthConstants;
import org.anyin.gitee.cloud.center.common.constants.GatewayConstants;
import org.anyin.gitee.cloud.lib.core.cache.CacheTemplate;
import org.anyin.gitee.cloud.lib.core.exceptions.CommonException;
import org.anyin.gitee.cloud.lib.core.utils.CollectionUtil;
import org.anyin.gitee.cloud.lib.core.web.ApiResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractNameValueGatewayFilterFactory;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.nio.charset.Charset;
import java.util.LinkedHashSet;

public class ValidateImageCodeGatewayFilter implements GatewayFilter {

    private AbstractNameValueGatewayFilterFactory.NameValueConfig config;

    private CacheTemplate cacheTemplate;

    public ValidateImageCodeGatewayFilter(AbstractNameValueGatewayFilterFactory.NameValueConfig config,
                                          CacheTemplate cacheTemplate) {
        this.config = config;
        this.cacheTemplate = cacheTemplate;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String originPath = this.getOriginPath(exchange);
        String validatePath = StringUtils.isEmpty(this.config.getValue()) ? AuthConstants.SYS_AUTH_LOGIN_PATH : this.config.getValue();
        if(!validatePath.equals(originPath)){
            return chain.filter(exchange);
        }
        try {
            this.check(exchange.getRequest());
        }catch (CommonException ex){
            ApiResponse resp = ApiResponse.error(ex.getErrorCode(), ex.getMessage());
            // 设置响应值
            return Mono.defer(() -> Mono.just(exchange.getResponse()))
                    .flatMap((response) -> {
                        response.setStatusCode(HttpStatus.OK);
                        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
                        DataBufferFactory dataBufferFactory = response.bufferFactory();
                        DataBuffer buffer = dataBufferFactory.wrap(JSONUtil.toJsonStr(resp).getBytes(Charset.defaultCharset()));
                        return response.writeWith(Mono.just(buffer)).doOnError((error) -> DataBufferUtils.release(buffer));
                    }
            );
        }
        return chain.filter(exchange);
    }

    /**
     * 验证码校验
     * @param request
     */
    private void check(ServerHttpRequest request){
        String code = request.getQueryParams().getFirst("code");
        if(StringUtils.isEmpty(code)){
            throw new CommonException("-1", "验证码不能为空");
        }
        String randomStr = request.getQueryParams().getFirst("randomStr");
        if(StringUtils.isEmpty(randomStr)){
            throw new CommonException("-1", "随机数不能为空");
        }
        String key = GatewayConstants.SYS_GATEWAY_CAPTCHA_KEY + randomStr;
        String text = cacheTemplate.valueGet(key, String.class);
        if(!code.equals(text)){
            throw new CommonException("-1", "验证码错误");
        }
        cacheTemplate.keyRemove(key);
    }

    /**
     * 获取实际路径
     * @param exchange 上下文
     * @return
     */
    private String getOriginPath(ServerWebExchange exchange){
        LinkedHashSet<URI> set = (LinkedHashSet<URI>)exchange.getAttributes().get(ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
        if(CollectionUtil.isEmpty(set)){
            return "";
        }
        String originPath = "";
        for(URI uri : set){
            originPath =  uri.getPath();
            break;
        }
        return originPath;
    }
}
